home *** CD-ROM | disk | FTP | other *** search
/ Cream of the Crop 26 / Cream of the Crop 26.iso / os2 / plnk081.zip / pilot-link.0.8.1 / sync-memodir.c < prev    next >
C/C++ Source or Header  |  1997-06-27  |  14KB  |  631 lines

  1. /* sync-memodir.c:  Memos stored in a directory
  2.  *
  3.  * Copyright (c) 1996, Kenneth Albanowski
  4.  *
  5.  * This is free software, licensed under the GNU Public License V2.
  6.  * See the file COPYING for details.
  7.  */
  8.  
  9. /* This program is a very rough take on synchronizing the memo pad on the Pilot
  10.    with a Unix directory,using the technique that Pace Williams mentioned.
  11.    
  12.    It creates a file "Memos.dir" and a directory "Memos/" in the _current
  13.    directory_!.
  14.    
  15.    "Memos" in turns contains "Backup" and "Archive" directories. Archived
  16.    items will be moved to the archive directory. Do not touch the Backup
  17.    directory, as its contents are integral to proper synchronization.
  18.    
  19.    Currently this code has full sync turned off, and will just copy the Pilot's
  20.    database into the directory. (I.e., "overwrite PC" is turned on.)
  21.    
  22.    Categories are not supported in any way.
  23.    
  24.    If you understand what is going on, feel free to hack or improve.
  25.    */
  26.  
  27. #include <stdio.h>
  28. #include <stdlib.h>
  29. #include <string.h>
  30. #include <ctype.h>
  31. #include <sys/types.h>
  32. #include <sys/stat.h>
  33.  
  34. #include "pi-source.h"
  35. #include "pi-socket.h"
  36. #include "pi-datebook.h"
  37. #include "pi-todo.h"
  38. #include "pi-dlp.h"
  39.  
  40. #include "pi-sync.h"
  41.  
  42. /* Define our own LocalRecord type. */
  43. struct LocalRecord {
  44.         StandardLocalRecord;
  45.         long ID;
  46.     time_t mtime;
  47.     char name[128];
  48.     LocalRecord * next;
  49. };
  50.  
  51. LocalRecord * memos = 0;
  52.  
  53. /* Define our own SyncAbs type */
  54. struct SyncAbs {
  55.     StandardSyncAbs;
  56.     /* items down here are equivalent of C++ class data.
  57.        Any information needed to talk to databases, etc., goes in here.
  58.        If we were doing multiple memo dbs, then memos would belong in here instead
  59.        of a global. */
  60. };
  61.  
  62. char * filename(char * memo) {
  63.     static char buf[256];
  64.     sprintf(buf,"Memos/%s",memo);
  65.     return buf;
  66. }
  67.  
  68. char * backupname(char * memo) {
  69.     static char buf[256];
  70.     sprintf(buf,"Memos/Backup/%s",memo);
  71.     return buf;
  72. }
  73.  
  74. char * newfilename(PilotRecord * r) {
  75.     static char name[256];
  76.     static char buf[256];
  77.     struct stat stbuf;
  78.     int i = 1;
  79.     char *rec, *end;
  80.  
  81.     rec = r->record;
  82.     end = &r->record[r->length];
  83.  
  84.     /* use first line as file name
  85.      * but change whitespace chars into '.'
  86.     */
  87.     while( rec < end && isspace(*rec) )        /* skip whitespace */
  88.         ++rec;
  89.  
  90.     for( i = 0; rec < end; ++i, ++rec) {
  91.         if( *rec == '\n' )
  92.             break;
  93.         else if( !isalnum(*rec) )
  94.             name[i] = '.';
  95.         else
  96.             name[i] = *rec;
  97.     }
  98.     name[i] = '\0';
  99.  
  100.     if( *name == '\0' )                /* an empty memo */
  101.         strcpy( name, "empty" );
  102.  
  103.     if (stat(filename(name), &stbuf) != 0)
  104.         return name;
  105.  
  106.     /* file name already exists, tack on a unique number */
  107.     for (i = 2; ; ++i) {
  108.         sprintf(buf, "%s,%d", name, i);
  109.         if (stat(filename(buf), &stbuf) != 0)
  110.             return buf;
  111.     }
  112. }
  113.  
  114. char * newarchivename(PilotRecord * r, LocalRecord * l) {
  115.     static char buf[256];
  116.     static int i = 1;
  117.     struct stat stbuf;
  118.     for(;;) {
  119.         sprintf(buf,"Memos/Archive/memo%d",i);
  120.         if(stat(buf,&stbuf)) {
  121.             break;
  122.         }
  123.         i++;
  124.     }
  125.     return buf;
  126. }
  127.  
  128. /*  These are the important functions below. They implement the interface that
  129.     the abstract synchronization layer invokes */
  130.  
  131. /* Get a Pilot ID for a local record, or 0 if no Pilot ID has been set. Any
  132.    local ID mechanism is not relevent, only IDs given by the Pilot. */
  133. unsigned long GetPilotID(SyncAbs * thisSA,LocalRecord * Local) {
  134.     return Local->ID;
  135.     /* return 1; */
  136. }
  137.  
  138. /* Set the ID on a local record to match a given Pilot ID. */
  139. int SetPilotID(SyncAbs * thisSA,LocalRecord * Local, unsigned long ID) {
  140.     Local->ID = ID;
  141.     return 1;
  142. }
  143.  
  144. /* Given a PilotRecord, try and find a local record with a matching ID */
  145. int MatchRecord(SyncAbs * thisSA,  LocalRecord ** Local, PilotRecord * p) {
  146.     LocalRecord * m = memos;
  147.     while(m) {
  148.         if(m->ID == p->ID)
  149.             break;
  150.         m=m->next;
  151.     }
  152.     if(m)
  153.         *Local = m;
  154.     else
  155.         *Local = 0;
  156.     
  157.     return 1;
  158. }
  159.  
  160. /* Free up the LocalRecord returned by MatchRecord */
  161. int FreeMatch(SyncAbs * thisSA,LocalRecord ** Local) {
  162.     *Local = 0;
  163.     return 0;
  164. }
  165.  
  166. /* Iterate over all LocalRecords, in arbitrary order */
  167. int Iterate(SyncAbs * thisSA, LocalRecord ** Local) {
  168.     LocalRecord * m = *Local;
  169.     if( !m) {
  170.         m = memos;
  171.     } else {
  172.         m=m->next;
  173.     }
  174.     *Local = m;
  175.     return m!=0;
  176. }
  177.  
  178. /* Iterate over local records of a specified type. */
  179. int IterateSpecific(SyncAbs * thisSA, LocalRecord ** Local, int flag, int archived) {
  180.     LocalRecord * m = *Local;
  181.     if( !m) {
  182.         m = memos;
  183.     } else {
  184.         m=m->next;
  185.     }
  186.     while(m) {
  187.             if(archived) {
  188.               if(m->archived)
  189.                 break;
  190.             } else {
  191.           if(m->attr == flag)
  192.             break;
  193.         }
  194.         m=m->next;
  195.     }
  196.     *Local = m;
  197.     return m!=0;
  198. }
  199.  
  200. /* Set status of local record */
  201. int SetStatus(SyncAbs * thisSA,LocalRecord * Local, int status) {
  202.     Local->attr = status;
  203.     return 0;
  204. }
  205.  
  206. /* There is no GetStatus, the abstract layer used Local->attr */
  207.  
  208. /* Set archival status of local record */
  209. int SetArchived(SyncAbs * thisSA,LocalRecord * Local,int archived) {
  210.         Local->archived = archived;
  211.     return 0;
  212. }
  213.  
  214. /* There is no GetStatus, the abstract layer used Local->archived */
  215.  
  216. /* Given a PilotRecord, store it in the local database */
  217. int StoreRemote(SyncAbs * thisSA,PilotRecord* p) {
  218.     LocalRecord * m;
  219.     int h;
  220.     struct stat stbuf;
  221.     
  222.     if(p->ID != 0) {
  223.         /* replace record */
  224.         m = memos;
  225.         while(m) {
  226.             if(m->ID == p->ID)
  227.                 break;
  228.             m=m->next;
  229.         }
  230.         if (m) {
  231.             h = open(filename(m->name),O_WRONLY|O_CREAT|O_TRUNC,0666);
  232.             write(h, p->record, p->length-1);
  233.             write(h, "\n", 1);
  234.             close(h);
  235.             stat(filename(m->name),&stbuf);
  236.             m->mtime = stbuf.st_mtime;
  237.             return 0;
  238.         }
  239.     }
  240.     /* new record */
  241.     m = (LocalRecord*)malloc(sizeof(LocalRecord));
  242.     m->ID = p->ID;
  243.     m->attr = p->attr;
  244.     m->secret = p->secret;
  245.     
  246.     strcpy(m->name, newfilename(p));
  247.     
  248.     m->next = memos;
  249.     memos = m;
  250.  
  251.     h = open(filename(m->name),O_WRONLY|O_CREAT|O_TRUNC,0666);
  252.     write(h, p->record, p->length-1);
  253.     write(h, "\n", 1);
  254.     close(h);
  255.  
  256.     stat(filename(m->name),&stbuf);
  257.     m->mtime = stbuf.st_mtime;
  258.     
  259.     return 0;    
  260. }
  261.  
  262. /* Given a local record, construct a PilotRecord suitable for transmission
  263. to a Pilot */
  264. PilotRecord * Transmit(SyncAbs* thisSA ,LocalRecord* Local) {
  265.         static PilotRecord p;
  266.     int h = open(filename(Local->name),O_RDONLY);
  267.     
  268.     struct stat statbuf;
  269.     stat(filename(Local->name),&statbuf);
  270.     p.length = statbuf.st_size+1;
  271.     p.record = (unsigned char*)malloc(p.length);
  272.     read(h, p.record, p.length-1);
  273.     p.record[p.length-1] = '\0';
  274.     close(h);
  275.     
  276.     p.category = 0;
  277.     p.attr = Local->attr;
  278.     p.archived = Local->archived;
  279.     p.secret = Local->secret;
  280.     
  281.     return &p;
  282. }
  283.  
  284. /* Free PilotRecord created by Transmit */
  285. int FreeTransmit(SyncAbs* thisSA,LocalRecord* Local,PilotRecord* Remote) {
  286.     free(Remote->record);
  287.     return 0;
  288. }
  289.  
  290. /* Find a local backup record and compare it to the pilot record for inequality */
  291. int CompareBackup(SyncAbs * thisSA, LocalRecord* m, PilotRecord* p) {
  292.     char buffer[0xffff];
  293.     int r,len;
  294.  
  295.       r = open(backupname(m->name),O_RDONLY);
  296.       
  297.       if (r<0)
  298.         return -1; /* "less", arbitrary */
  299.       
  300.       len = read(r,buffer,0xffff);
  301.     close(r);
  302.     
  303.     if(len != p->length)
  304.         return -1; /* "less", arbitrary */
  305.  
  306.     if(buffer[len-1] == '\n')
  307.         buffer[len-1] = '\0';
  308.     
  309.     return memcmp(buffer, p->record, len);
  310. }
  311.  
  312. /* Compare a local record and pilot record for inequality */
  313. int Compare(SyncAbs * thisSA, LocalRecord * m, PilotRecord* p) {
  314.     char buffer[0xffff];
  315.       int r = open(filename(m->name),O_RDONLY);
  316.       int len = read(r,buffer,0xffff);
  317.     close(r);
  318.  
  319.     if(len != p->length)
  320.         return -1; /* "less", arbitrary */
  321.  
  322.     if(buffer[len-1] == '\n')
  323.         buffer[len-1] = '\0';
  324.     
  325.     return memcmp(buffer, p->record, len);
  326. }
  327.  
  328. /* Delete all local records */
  329. int DeleteAll(SyncAbs * thisSA) {
  330.     while(memos) {
  331.         LocalRecord * m = memos;
  332.         memos = memos->next;
  333.         if(m->attr != RecordDeleted) {
  334.             unlink(filename(m->name));
  335.         }
  336.         free(m);
  337.     }
  338.     return 0;
  339. }
  340.  
  341. /* Do a local purge, deleting all records marked Deleted, and
  342.    archiving all records marked for archiving */
  343. int Purge(SyncAbs * thisSA) {
  344.     LocalRecord * prev = 0;
  345.     LocalRecord * m = memos, *next;
  346.     while(m) {
  347.         next = m->next;
  348.         if((m->attr == RecordDeleted) || (m->archived)) {
  349.             unlink(filename(m->name));
  350.             if(prev)
  351.                 prev->next = next;
  352.             else
  353.                 memos=next;
  354.             free(m);
  355.         } else
  356.             prev = m;
  357.         m = next;
  358.     }
  359.     return 0;
  360. }
  361.  
  362. /* Add remote record to archive. l is non-NULL if there is a matching local record */
  363. int ArchiveRemote(SyncAbs * s, LocalRecord * l, PilotRecord * p) {
  364.     char name[256];
  365.     int h;
  366.     strcpy(name,"Memos/Archive/memoXXXXXX");
  367.  
  368.     h = open(newarchivename(p,l),O_WRONLY|O_CREAT|O_TRUNC,0666);
  369.     write(h, p->record, p->length-1);
  370.     write(h, "\n", 1);
  371.     close(h);
  372.     
  373.     return 0;
  374. }
  375.  
  376. struct file {
  377.     char name[128];
  378.     struct file * next;
  379. };
  380.  
  381. struct file * files = 0;
  382.  
  383. int main(int argc, char *argv[])
  384. {
  385.   struct pi_sockaddr addr;
  386.   int db;
  387.   int sd;
  388.   struct PilotUser U;
  389.   struct dirent * dirent;
  390.   DIR * d;
  391.   int ret;
  392.   FILE * f;
  393.   struct SyncAbs abs;
  394.  
  395.   /* Set up abstraction structure */  
  396.   abs.MatchRecord = MatchRecord;
  397.   abs.Iterate = Iterate;
  398.   abs.IterateSpecific = IterateSpecific;
  399.   abs.SetStatus = SetStatus;
  400.   abs.SetArchived = SetArchived;
  401.   abs.SetPilotID = SetPilotID;
  402.   abs.GetPilotID = GetPilotID;
  403.   abs.StoreRemote = StoreRemote;
  404.   abs.ArchiveLocal = 0;             /* missing */
  405.   abs.ClearStatusArchiveLocal = 0;  /* likewise */
  406.   abs.ArchiveRemote = ArchiveRemote;
  407.   abs.DeleteAll = DeleteAll;
  408.   abs.Purge = Purge;
  409.   abs.CompareBackup = CompareBackup;
  410.   abs.Compare = Compare;
  411.   abs.Transmit = Transmit;
  412.   abs.FreeTransmit = FreeTransmit;
  413.  
  414.   fprintf(stderr, "A Warning be upon Ye: Here Be Dragons!\nThis program is incomplete, ill-considered, and unreliable!\nDo not Rely on without Consideration of Completing the program...\n\n");
  415.   
  416.   if (argc < 2) {
  417.     fprintf(stderr,"usage:%s %s # A directory called Memos will be created!\n",argv[0],TTYPrompt);
  418.     exit(2);
  419.   }
  420.   if (!(sd = pi_socket(PI_AF_SLP, PI_SOCK_STREAM, PI_PF_PADP))) {
  421.     perror("pi_socket");
  422.     exit(1);
  423.   }
  424.   
  425.   mkdir("Memos",0700);
  426.   mkdir("Memos/Archive",0700);
  427.   mkdir("Memos/Backup",0700);
  428.   
  429.   d = opendir("Memos");
  430.   while( (dirent = readdir(d)) ) {
  431.       struct file * f = (struct file*)malloc(sizeof(struct file));
  432.       if(dirent->d_name[0] == '.')
  433.           continue;
  434.       if(strcmp(dirent->d_name,"Archive")==0)
  435.           continue;
  436.       if(strcmp(dirent->d_name,"Backup")==0)
  437.           continue;
  438.       if(strcmp(dirent->d_name,"Memos.dir")==0)
  439.           continue;
  440.       if(dirent->d_name[strlen(dirent->d_name)-1] == '~')
  441.           continue;
  442.       f->next = files;
  443.       files = f;
  444.       strcpy(f->name, dirent->d_name);
  445.       printf("|%s|\n", f->name);
  446.   }
  447.   closedir(d);
  448.   
  449.   f = fopen(filename("Memos.dir"),"r");
  450.   while (f && !feof(f)) {
  451.       long l;
  452.       LocalRecord * m = (LocalRecord*)malloc(sizeof(LocalRecord));
  453.       if(fscanf(f, "%lu %lu %s", &m->ID, &l, &m->name[0])<3)
  454.           break;
  455.       m->mtime = (time_t)l;
  456.       m->attr = RecordNothing;
  457.       m->secret = 0;
  458.       m->archived = 0;
  459.       m->next = memos;
  460.       printf("Read in memo '%s'\n",m->name);
  461.       memos = m;
  462.   }
  463.   fclose(f);
  464.   
  465.   /* Iterate over files in Memos directory, recording new and modified memos */
  466.   {
  467.       struct file * f = files;
  468.       while(f) {
  469.           LocalRecord * m = memos;
  470.         struct stat stbuf;
  471.         char buf[256];
  472.         sprintf(buf,"Memos/%s",f->name);
  473.         stat(buf, &stbuf);
  474.         
  475.           while(m) {
  476.               if(strcmp(m->name,f->name)==0)
  477.                   break;
  478.               m=m->next;
  479.           }
  480.           if(!m) {
  481.               m = (LocalRecord*)malloc(sizeof(LocalRecord));
  482.               strcpy(m->name, f->name);
  483.               m->ID = 0;
  484.               m->attr = RecordNew;
  485.               m->secret = 0;
  486.               m->archived = 0;
  487.               m->next = memos;
  488.             printf("Memo %s is new\n", m->name);
  489.               memos = m;
  490.           } else {
  491.             if(stbuf.st_mtime > m->mtime) {
  492.                 m->attr = RecordModified;
  493.                 printf("Memo %s is modified\n", m->name);
  494.             }
  495.           }
  496.         m->mtime = stbuf.st_mtime;
  497.           f=f->next;
  498.       }
  499.   }
  500.  
  501.   /* Iterate over memo list, marking deleted memos */
  502.   {
  503.       LocalRecord * m = memos;
  504.       while(m) {
  505.           struct file * f = files;
  506.           while(f) {
  507.               if(strcmp(m->name,f->name)==0)
  508.                   break;
  509.               f=f->next;
  510.           }
  511.           if(!f) {
  512.               m->attr = RecordDeleted;
  513.             printf("Memo %s is deleted\n", m->name);
  514.           }
  515.           m=m->next;
  516.       }
  517.   }
  518.   
  519.  
  520.   addr.pi_family = PI_AF_SLP;
  521.   strcpy(addr.pi_device,argv[1]);
  522.   
  523.   ret = pi_bind(sd, (struct sockaddr*)&addr, sizeof(addr));
  524.   if(ret == -1) {
  525.     perror("pi_bind");
  526.     exit(1);
  527.   }
  528.  
  529.   ret = pi_listen(sd,1);
  530.   if(ret == -1) {
  531.     perror("pi_listen");
  532.     exit(1);
  533.   }
  534.  
  535.   sd = pi_accept(sd, 0, 0);
  536.   if(sd == -1) {
  537.     perror("pi_accept");
  538.     exit(1);
  539.   }
  540.  
  541.   /* Ask the pilot who it is. */
  542.   dlp_ReadUserInfo(sd,&U);
  543.   
  544.   /* Tell user (via Pilot) that we are starting things up */
  545.   dlp_OpenConduit(sd);
  546.  
  547.  
  548. #if 0
  549.   if (dlp_CreateDB(sd, 'KeAl', 'data', 0, 0, 1, "XMemoDB", &db)<0) {
  550.     dlp_OpenDB(sd, 0, dlpOpenRead|dlpOpenWrite, "XMemoDB", &db);
  551.   }
  552. #endif
  553.                  
  554.   /* Open the Datebook's database, store access handle in db */
  555.   if(dlp_OpenDB(sd, 0, dlpOpenRead|dlpOpenWrite, "MemoDB", &db) < 0) {
  556.     puts("Unable to open MemoDB");
  557.     dlp_AddSyncLogEntry(sd, "Unable to open MemoDB.\n");
  558.     pi_close(sd);
  559.     exit(1);
  560.   }
  561.  
  562. #if 1  
  563.   /* Trigger overwrite of local with remote data. */
  564.   CopyFromRemote(sd, db, &abs);
  565. #endif
  566.  
  567. #if 0
  568.   /* Trigger overwrite of remote with local data. */
  569.   CopyToRemote(sd, db, &abs);
  570. #endif
  571.  
  572. #if 0
  573.   /* For testing slow syncs, get rid of the flags, since we don't need them */
  574.   dlp_ResetSyncFlags(sd, db);
  575.  
  576.   SlowSync(sd, db, &abs);
  577.  
  578.   /* Reset the flags now that we are done with them. */
  579.   dlp_ResetSyncFlags(sd, db);
  580. #endif
  581.  
  582.   /* Close the database */
  583.   dlp_CloseDB(sd, db);
  584.   
  585.   /* Delete previous backup */
  586.   d = opendir("Memos/Backup");
  587.   while( (dirent = readdir(d)) ) {
  588.         char name[256];
  589.       if(dirent->d_name[0] == '.')
  590.           continue;
  591.       sprintf(name,"Memos/Backup/%s", dirent->d_name);
  592.       unlink(name);
  593.   }
  594.   closedir(d);
  595.  
  596.   /* Backup current memos */
  597.   {
  598.       LocalRecord * m = memos;
  599.       while(m) {
  600.           char buffer[0xffff];
  601.           int r = open(filename(m->name),O_RDONLY);
  602.           int w = open(backupname(m->name),O_WRONLY|O_CREAT|O_TRUNC,0600);
  603.           write(w,buffer, read(r,buffer,0xffff));
  604.           close(r);
  605.           close(w);
  606.           m=m->next;
  607.       }
  608.   }
  609.  
  610.   dlp_AddSyncLogEntry(sd, "Read memos from Pilot.\n");
  611.  
  612.   dlp_ReadUserInfo(sd, &U);
  613.   
  614.   U.lastSyncPC = 0xDEADBEEF;
  615.   
  616.   dlp_WriteUserInfo(sd, &U);
  617.     
  618.   pi_close(sd);  
  619.  
  620.   /* Rewrite memo index */
  621.   f = fopen(filename("Memos.dir"),"w");
  622.   while(memos) {
  623.       fprintf(f,"%lu %lu %s\n", memos->ID, (unsigned long)memos->mtime, memos->name);
  624.       memos = memos->next;
  625.   }
  626.   fclose(f);
  627.  
  628.   return 0;
  629. }
  630.  
  631.